package com.copm.ifm.servers.sys.service.impl.project;

import cn.hutool.core.text.StrPool;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.copm.ifm.base.api.pojo.PageDTO;
import com.copm.ifm.base.api.pojo.PageVO;
import com.copm.ifm.base.basic.ServiceResponse;
import com.copm.ifm.base.basic.constant.DbConstants;
import com.copm.ifm.base.basic.enums.ResponseCodeEnum;
import com.copm.ifm.base.basic.exception.ServiceException;
import com.copm.ifm.base.basic.util.AuthUtils;
import com.copm.ifm.base.basic.util.DateUtils;
import com.copm.ifm.base.basic.util.ObjectUtils;
import com.copm.ifm.base.service.mp.page.PageProxy;
import com.copm.ifm.servers.org.client.OrgProjectLevelClient;
import com.copm.ifm.servers.org.entity.project.OrgProjectLevel;
import com.copm.ifm.servers.sys.dto.SysOutProjectDTO;
import com.copm.ifm.servers.sys.dto.SysProjectDTO;
import com.copm.ifm.servers.sys.entity.SysBusinessType;
import com.copm.ifm.servers.sys.entity.SysProject;
import com.copm.ifm.servers.sys.entity.SysProjectAuth;
import com.copm.ifm.servers.sys.entity.SysProjectDetail;
import com.copm.ifm.servers.sys.entity.area.SysAreaAuth;
import com.copm.ifm.servers.sys.mapper.project.SysProjectAuthMapper;
import com.copm.ifm.servers.sys.mapper.project.SysProjectDetailMapper;
import com.copm.ifm.servers.sys.mapper.project.SysProjectMapper;
import com.copm.ifm.servers.sys.mapping.SysProjMapping;
import com.copm.ifm.servers.sys.service.area.SysAreaAuthService;
import com.copm.ifm.servers.sys.service.project.SysProjectAuthService;
import com.copm.ifm.servers.sys.service.project.SysProjectService;
import com.copm.ifm.servers.sys.service.type.SysBusinessTypeService;
import com.copm.ifm.servers.sys.vo.SysProjectAuthVO;
import com.copm.ifm.servers.sys.vo.SysProjectVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 项目表 服务实现类
 *
 * @author zzf
 * @since 2020-12-14
 */
@Slf4j
@Service
public class SysProjectServiceImpl extends ServiceImpl<SysProjectMapper, SysProject> implements SysProjectService {

    @Resource
    SysProjectMapper sysProjectMapper;

    @Resource
    SysProjectAuthMapper sysProjectAuthMapper;

    @Resource
    SysProjectAuthService sysProjectAuthService;

    @Resource
    SysBusinessTypeService sysBusinessTypeService;

    @Resource
    SysProjectDetailMapper sysProjectDetailMapper;

    @Resource
    SysAreaAuthService sysAreaAuthService;

    @Autowired
    OrgProjectLevelClient orgProjectLevelClient;

    @Override
    public PageVO<SysProjectVO> selectForPage(PageDTO<SysProjectDTO> pageDTO) {
        if ("proName".equals(pageDTO.getSortName())) {
            pageDTO.setSortName("CONVERT( proName USING gbk )");
        }
        PageVO<SysProjectVO> proxy = PageProxy.proxy(baseMapper::selectForPage, pageDTO);
        List<SysProjectVO> records = proxy.getRecords();
        ServiceResponse<List<SysProjectVO>> success = new ServiceResponse<>(records, ResponseCodeEnum.SUCCESS);
        proxy.setRecords(success.getData());
        return proxy;
    }

    public PageVO<SysProjectVO> selectTenantAuthForPage(PageDTO<SysProjectDTO> pageDTO) {
        return PageProxy.proxy(baseMapper::selectProjectDetailForPage, pageDTO);
    }

    public int selectCountTenantForPage(PageDTO<SysProjectDTO> pageDTO) {
        return sysProjectMapper.selectCountProjectForPage(pageDTO.getParam());
    }

    @Override
    public SysProjectVO getProjectVoById(Long id) {
        return sysProjectMapper.getProjectVOById(id);
    }

    /**
     * 编辑保存项目表
     * 编辑保存保存校验规则： “城市+项目名称”唯一； 项目编码，整个系统唯一；必填校验；
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ServiceResponse<?> updateProjectVoById(SysProjectDTO entity) {
        int countN = sysProjectMapper.checkProjectId(entity.getCity(), entity.getProName());
        int countC = sysProjectMapper.checkProjectCode(entity.getProCode());
        SysProject project = sysProjectMapper.selectById(entity.getId());
        if (countN > 1 || (countN > 0 && !entity.getProName().equals(project.getName()))) {
            throw new ServiceException("新增项目名【" + entity.getProName() + "】已存在，请修改后提交");
        }
        if (countC > 1 || (countC > 0 && !entity.getProCode().equals(project.getCode()))) {
            throw new ServiceException("新增项目code【" + entity.getProCode() + "】已存在，请修改后提交");
        }
        SysProject sysProject = SysProjMapping.INSTANCE.DTO2PO(entity);
        sysProjectMapper.updateByProjectId(sysProject);
        log.info("插入SysProjectVO中sysProject：" + sysProject);
        SysProjectDetail sysProjectDetail = SysProjMapping.INSTANCE.DTO2Detail(entity);
        sysProjectDetailMapper.updateByProjectId(sysProjectDetail);
        log.info("插入SysProjectVO中sysProjectDetail：" + sysProjectDetail);

        return new ServiceResponse<>(entity, ResponseCodeEnum.SUCCESS);
    }

    /**
     * 新增项目表
     * 保存校验规则： “城市+项目名称”唯一； 项目编码，整个系统唯一；必填校验；
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ServiceResponse<?> saveSysProjectVO(SysProjectDTO entity) {
        //保存校验
        int countN = sysProjectMapper.checkProjectId(entity.getCity(), entity.getProName());
        int countC = sysProjectMapper.checkProjectCode(entity.getProCode());
        if (countN > 0) {
            throw new ServiceException("新增项目名【" + entity.getProName() + "】已存在，请修改后提交");
        }
        if (countC > 0) {
            throw new ServiceException("新增项目code【" + entity.getProCode() + "】已存在，请修改后提交");
        }
        SysProject sysProject = SysProjMapping.INSTANCE.DTO2PO(entity);
        sysProjectMapper.insert(sysProject);

        entity.setId(sysProject.getId());
        SysProjectDetail sysProjectDetail = SysProjMapping.INSTANCE.DTO2Detail(entity);
        sysProjectDetailMapper.insert(sysProjectDetail);
        return new ServiceResponse<>(entity, ResponseCodeEnum.SUCCESS);
    }

    @Override
    public SysProjectAuth selectForAuth(int projectId) {
        return sysProjectAuthMapper.selectById(projectId);
    }

    @Override
    public PageVO<SysProjectAuthVO> selectAuthForPage(PageDTO<SysProjectAuth> pageDTO) {
        PageVO<SysProjectAuthVO> proxy = PageProxy.proxy(sysProjectAuthMapper::selectForPage, pageDTO);
        List<SysProjectAuthVO> record = proxy.getRecords();
        ServiceResponse<List<SysProjectAuthVO>> success = new ServiceResponse<>(record, ResponseCodeEnum.SUCCESS);
        proxy.setRecords(success.getData());
        List<SysProjectAuthVO> records = proxy.getRecords();
        records.forEach(s -> {
            //检查每项的时间是否过期，如果过期将is_overdue设置成已过期
            boolean isEffect = true;
            //两个都不能为空，再判断是否在当前时间内，如果在时间内就不管下面，不在时间为否，修改状态为1
            if (!StringUtils.isEmpty(s.getAuthStartTime()) && !StringUtils.isEmpty(s.getAuthEndTime())) {
                isEffect = DateUtils.isBetweenDate(new Date(), s.getAuthStartTime(), s.getAuthEndTime());
            }
            //考虑两个时间都大于当前时间的情况；
            if (s.getAuthStartTime().after(new Date()) || s.getAuthEndTime().before(new Date())) {
                isEffect = true;
            }
            if (!isEffect) {
                SysProjectAuth sysProjectAuth = new SysProjectAuth();
                sysProjectAuth.setIsOverdue(true);
                sysProjectAuth.setId(s.getId());
                s.setIsOverdue(true);
                sysProjectAuthMapper.updateById(sysProjectAuth);
            }
        });
        records.forEach(x -> {
            List<String> list;
            String[] split = x.getBusinessTypes().split(",");
            list = Arrays.stream(split).map(s -> {
                SysBusinessType businessType = sysBusinessTypeService.getOne(new LambdaQueryWrapper<SysBusinessType>()
                        .eq(SysBusinessType::getCode, s)
                        .eq(SysBusinessType::getTenantId, x.getTenantId()));
                if (ObjectUtils.isNull(businessType)) {
                    throw new ServiceException("没有对应的业务类型权限");
                }
                return businessType.getName();
            }).collect(Collectors.toList());
            x.setBusinessTypeList(list);
        });

        proxy.setRecords(records);
        return proxy;
    }

    @Override
    public SysProjectAuthVO getAuthTenantById(Long id) {
        SysProjectAuthVO sysProjectAuthVO = sysProjectAuthMapper.getAuthTenantById(id);
        String[] split = sysProjectAuthVO.getBusinessTypes().split(",");
        List<String> list = Arrays.stream(split).map(s -> sysBusinessTypeService.getOne(new LambdaQueryWrapper<SysBusinessType>()
                .eq(SysBusinessType::getTenantId, sysProjectAuthVO.getTenantId())
                .eq(SysBusinessType::getCode, s)).getName()).collect(Collectors.toList());
        sysProjectAuthVO.setBusinessTypeList(list);
        return sysProjectAuthVO;
    }

    /**
     * 项目授权（单个以及多个授权接口）
     */
    @Override
    public List<SysProjectAuth> authTenant(String projectList, SysProjectAuth sysProjectAuth, String businessTypes) {
        Date startTime = sysProjectAuth.getAuthStartTime();
        Date endTime = sysProjectAuth.getAuthEndTime();
        if (ObjectUtils.isNull(startTime) || ObjectUtils.isNull(endTime)) {
            throw new ServiceException("请填写对应的 起始 或者 结束 时间");
        }
        if (endTime.before(startTime)) {
            throw new ServiceException("时间填写不正确，开始日期大于截止日期");
        }
        List<SysProjectAuth> sysProjectAuthList = new ArrayList<>();
        String[] split = checkTime(projectList, sysProjectAuth);
        checkDate(split, sysProjectAuthList, startTime, endTime, sysProjectAuth, businessTypes);
        sysProjectAuthMapper.insertList(sysProjectAuthList);
        try {
            sysProjectAuthList.forEach(s -> {
                SysProject sysProject = sysProjectMapper.selectOne(new LambdaQueryWrapper<SysProject>()
                        .eq(SysProject::getId, s.getProjectId())
                );
                OrgProjectLevel projectLevel = transform(sysProject);
                orgProjectLevelClient.add(projectLevel);
            });
        } catch (Exception e) {
            log.info("系统后台服务方调用企业组织管理失败");
        }
        return sysProjectAuthList;
    }

    private OrgProjectLevel transform(SysProject sysProject) {
        OrgProjectLevel orgProjectLevel = new OrgProjectLevel();
        orgProjectLevel.setProjectId(sysProject.getId());
        orgProjectLevel.setCode(sysProject.getCode());
        orgProjectLevel.setCreateBy(sysProject.getCreateBy());
        orgProjectLevel.setName(sysProject.getName());
        orgProjectLevel.setTenantId(sysProject.getTenantId());
        orgProjectLevel.setRemark(sysProject.getRemark());
        orgProjectLevel.setCreateTime(sysProject.getCreateTime());
        orgProjectLevel.setLatitude(sysProject.getLatitude());
        orgProjectLevel.setLongitude(sysProject.getLongitude());
        return orgProjectLevel;
    }

    /**
     * 更新权限的方式是删除当前选中的id集合，然后再重新添加（这个方式当时是为了批量添加）
     *
     * @param ids            权限集合，用逗号隔开
     * @param projectIds     项目id的字符串，用逗号隔开
     * @param sysProjectAuth 前端传来的对象（包括授权的前后时间点）
     */
    @Override
    public List<SysProjectAuth> updateAuthTenant(String ids, String projectIds, SysProjectAuth sysProjectAuth, String businessTypes) {
        //这里是获取前端定义的授权时间区间值
        Date startTime = sysProjectAuth.getAuthStartTime();
        Date endTime = sysProjectAuth.getAuthEndTime();
        String[] auths = ids.split(StrPool.COMMA);
        List<Integer> idList = new ArrayList<>();
        Arrays.stream(auths).forEach(s -> {
            Integer id = Integer.valueOf(s);
            idList.add(id);
        });
        sysProjectAuthMapper.deleteBatchIds(idList);
        String[] split = checkTime(projectIds, sysProjectAuth);
        List<SysProjectAuth> sysProjectAuthList = new ArrayList<>();
        checkDate(split, sysProjectAuthList, startTime, endTime, sysProjectAuth, businessTypes);
        sysProjectAuthList.forEach(s -> s.setUpdateTime(new Date()));
        sysProjectAuthService.saveBatch(sysProjectAuthList);
        return sysProjectAuthList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOutProjectAuth(SysOutProjectDTO sysOutProjectDTO, String areas) {
        Date startTime = sysOutProjectDTO.getAuthStartTime();
        Date endTime = sysOutProjectDTO.getAuthEndTime();
        if (startTime.after(endTime)) {
            throw new ServiceException("保存的起始时间大于结束时间，请重新填写");
        }
        //A授权给B的A tenantId（要求这两个时间不能超过A这个项目的所有授权时间）
        Long tenantId = sysOutProjectDTO.getTenantId();
        List<SysProjectAuth> sysProjectAuthList = sysProjectAuthService.list(new LambdaQueryWrapper<SysProjectAuth>()
                .eq(SysProjectAuth::getTenantId, tenantId)
                .eq(SysProjectAuth::getProjectId, sysOutProjectDTO.getProjectId())
                .eq(SysProjectAuth::getIsOverdue, DbConstants.DEFAULT_NO));
        List<SysProjectAuth> collect = sysProjectAuthList.stream()
                .filter(s -> DateUtils.isBetweenDate(startTime, s.getAuthStartTime(), s.getAuthEndTime()))
                .collect(Collectors.toList());
        if (collect.size() < 1) {
            throw new ServiceException("该时间段的租户没有授权的项目，请重新填写时间");
        }
        if (endTime.after(collect.get(0).getAuthEndTime())) {
            throw new ServiceException("填写的截止授权时间超出项目已有授权时间，请重新填写时间");
        }
        SysProjectAuth projectAuth = out2ProjectAuth(sysOutProjectDTO);
        sysProjectAuthMapper.insert(projectAuth);

        List<String> areaList = Arrays.asList(areas.split(","));
        List<Long> areaIdList = areaList.stream().map(Long::parseLong).collect(Collectors.toList());
        List<SysAreaAuth> sysAreaAuths = new ArrayList<>();
        areaIdList.forEach(s -> {
            //保存供应商和空间的对外授权关联表
            SysAreaAuth sysAreaAuth = new SysAreaAuth();
            sysAreaAuth.setAreaId(s);
            sysAreaAuth.setProjectId(sysOutProjectDTO.getProjectId());
            //这里只考虑给单个供应商授权的情况（如果是多个则是批量添加的方式）
            sysAreaAuth.setTenantId(Long.valueOf(sysOutProjectDTO.getSupplier()));
            sysAreaAuth.setCreateBy(AuthUtils.getCurrentUserId());
            sysAreaAuth.setCreateTime(new Date());
            sysAreaAuths.add(sysAreaAuth);
        });
        sysAreaAuthService.saveBatch(sysAreaAuths);
    }

    private SysProjectAuth out2ProjectAuth(SysOutProjectDTO sysOutProjectDTO) {
        SysProjectAuth sysProjectAuth = new SysProjectAuth();
        sysProjectAuth.setProjectId(sysOutProjectDTO.getProjectId());
        sysProjectAuth.setCreateTime(new Date());
        sysProjectAuth.setAuthStartTime(sysOutProjectDTO.getAuthStartTime());
        sysProjectAuth.setAuthEndTime(sysOutProjectDTO.getAuthEndTime());
        sysProjectAuth.setBusinessTypes(sysOutProjectDTO.getBusinessTypes());
        sysProjectAuth.setCreateBy(AuthUtils.getCurrentUserId());
        sysProjectAuth.setTenantId(sysOutProjectDTO.getTenantId());
        sysProjectAuth.setIsOverdue(false);
        sysProjectAuth.setIsExternalAuth(true);
        sysProjectAuth.setSupplier(sysOutProjectDTO.getSupplier());
        sysProjectAuth.setOverdueTime(sysOutProjectDTO.getOverdueTime());
        return sysProjectAuth;
    }


    /**
     * 校验开始时间和结束时间的合理性
     *
     * @param projectList    项目id的字符串，用逗号隔开
     * @param sysProjectAuth 前端传来的对象（包括授权的前后时间点）
     */
    public String[] checkTime(String projectList, SysProjectAuth sysProjectAuth) {
        Date startTime = sysProjectAuth.getAuthStartTime();
        Date endTime = sysProjectAuth.getAuthEndTime();
        if (endTime.before(startTime)) {
            throw new ServiceException("时间填写不正确，开始日期大于截止日期");
        }
        return projectList.split(StrPool.COMMA);
    }

    /**
     * 校验同项目下的授权租户的时间是否重合，并加入集合
     */
    public void checkDate(String[] split, List<SysProjectAuth> sysProjectAuthList,
                          Date startTime, Date endTime, SysProjectAuth sysProjectAuth, String businessTypes) {
        for (String s : split) {
            Long projectId = Long.valueOf(s);
            //对每个项目id进行校验
            List<SysProjectAuth> projectAuthList = sysProjectAuthMapper.selectList(
                    new LambdaQueryWrapper<SysProjectAuth>()
                            .eq(SysProjectAuth::getProjectId, projectId)
                            .eq(SysProjectAuth::getIsOverdue, true));
            projectAuthList.forEach(x -> {
                if (!(StringUtils.isEmpty(x.getAuthStartTime()) || StringUtils.isEmpty(x.getAuthEndTime()))) {
                    boolean isStart = DateUtils.isBetweenDate(startTime, x.getAuthStartTime(), x.getAuthEndTime());
                    boolean isEnd = DateUtils.isBetweenDate(endTime, x.getAuthStartTime(), x.getAuthEndTime());
                    if (isStart || isEnd) {
                        String proName = sysProjectMapper.selectOne(new LambdaQueryWrapper<SysProject>()
                                .eq(SysProject::getId, x.getProjectId())
                        ).getName();
                        throw new ServiceException(proName + "项目的授权期与现有的有效的授权期存在重叠，请检查确认后提交");
                    }
                }
            });
            sysProjectAuth.setProjectId(projectId);
            sysProjectAuth.setAuthEndTime(endTime);
            sysProjectAuth.setCreateTime(new Date());
            sysProjectAuth.setAuthStartTime(startTime);
            sysProjectAuth.setUpdateBy(AuthUtils.getCurrentUserId());
            sysProjectAuth.setBusinessTypes(businessTypes);
            sysProjectAuthList.add(sysProjectAuth);
        }
    }

    /**
     * 多项目授权校验
     * <p>
     * projectList项目id的集合（例如：1，2，3，4....）
     */
    @Override
    public String checkTenant(String projectList, Long tenantId) {
        StringBuffer sb = new StringBuffer();
        Arrays.stream(projectList.split(StrPool.COMMA)).forEach(s -> {
            Integer projectId = Integer.valueOf(s);
            List<SysProjectAuth> sysProjectAuthList = sysProjectAuthMapper.selectList(
                    new LambdaQueryWrapper<SysProjectAuth>()
                            .eq(SysProjectAuth::getProjectId, projectId)
            );
            Set<Long> collect = sysProjectAuthList.stream().map(SysProjectAuth::getTenantId).collect(Collectors.toSet());
            List<Long> list = new ArrayList<>();
            if (collect.size() > 0) {
                collect.forEach(x -> {
                    if (!x.equals(tenantId)) {
                        list.add(x);
                    }
                });
            }
            if (list.size() > 0) {
                SysProject sysProject = sysProjectMapper.selectById(projectId);
                String projectName = sysProject.getName();
                if (sysProjectAuthList.size() > 0) {
                    sb.append(projectName).append(StrPool.COMMA);
                }
            }
        });
        if ("".equals(sb.toString())) {
            return "0";
        }
        String s = sb.toString();
        return s.substring(0, s.length() - 1);
    }

}
